import requests
import json
import logging
from typing import Optional, Dict, Any
from .cm_user import CmUser
from init import db
import os
import time
from dotenv import load_dotenv

load_dotenv()

# 获取日志记录器
logger = logging.getLogger(__name__)

class WeChatService:
    def __init__(self):
        self.app_id = os.getenv('WECHAT_APP_ID')
        self.app_secret = os.getenv('WECHAT_APP_SECRET')
        
    def get_access_token(self, code: str) -> Optional[Dict[str, Any]]:
        """
        通过code获取微信access_token和openid (网页授权)
        """
        if not self.app_id or not self.app_secret:
            raise ValueError("微信配置未设置，请检查WECHAT_APP_ID和WECHAT_APP_SECRET环境变量")
            
        url = "https://api.weixin.qq.com/sns/oauth2/access_token"
        params = {
            'appid': self.app_id,
            'secret': self.app_secret,
            'code': code,
            'grant_type': 'authorization_code'
        }
        
        try:
            logger.info("使用网页授权API获取access_token")
            response = requests.get(url, params=params)
            response.raise_for_status()
            result = response.json()
            
            if 'errcode' in result:
                raise Exception(f"微信API错误: {result.get('errmsg', '未知错误')}")
                
            return result
        except requests.exceptions.RequestException as e:
            raise Exception(f"请求微信API失败: {str(e)}")
        except json.JSONDecodeError as e:
            raise Exception(f"解析微信API响应失败: {str(e)}")
    
    def get_miniprogram_session(self, code: str) -> Optional[Dict[str, Any]]:
        """
        通过code获取微信小程序session_key和openid (小程序授权)
        """
        if not self.app_id or not self.app_secret:
            raise ValueError("微信配置未设置，请检查WECHAT_APP_ID和WECHAT_APP_SECRET环境变量")
            
        url = "https://api.weixin.qq.com/sns/jscode2session"
        params = {
            'appid': self.app_id,
            'secret': self.app_secret,
            'js_code': code,
            'grant_type': 'authorization_code'
        }
        
        try:
            logger.info("使用小程序授权API获取session_key")
            response = requests.get(url, params=params)
            response.raise_for_status()
            result = response.json()
            
            if 'errcode' in result:
                raise Exception(f"微信API错误: {result.get('errmsg', '未知错误')}")
                
            return result
        except requests.exceptions.RequestException as e:
            raise Exception(f"请求微信API失败: {str(e)}")
        except json.JSONDecodeError as e:
            raise Exception(f"解析微信API响应失败: {str(e)}")
    
    def get_user_info(self, access_token: str, openid: str) -> Optional[Dict[str, Any]]:
        """
        获取微信用户信息
        """
        url = "https://api.weixin.qq.com/sns/userinfo"
        params = {
            'access_token': access_token,
            'openid': openid,
            'lang': 'zh_CN'
        }
        
        try:
            response = requests.get(url, params=params)
            response.raise_for_status()
            result = response.json()
            
            if 'errcode' in result:
                raise Exception(f"获取用户信息失败: {result.get('errmsg', '未知错误')}")
                
            return result
        except requests.exceptions.RequestException as e:
            raise Exception(f"请求用户信息失败: {str(e)}")
        except json.JSONDecodeError as e:
            raise Exception(f"解析用户信息响应失败: {str(e)}")
    
    def login_with_code(self, code: str) -> Dict[str, Any]:
        """
        使用微信code进行登录 (支持网页授权和小程序授权)
        """
        try:
            # 首先尝试小程序授权
            logger.info("尝试使用小程序授权方式")
            session_info = self.get_miniprogram_session(code)
            openid = session_info.get('openid')
            session_key = session_info.get('session_key')
            unionid = session_info.get('unionid')
            
            if not openid:
                raise Exception("获取微信授权信息失败")
            
            logger.info("小程序授权成功")
            
            # 小程序登录不需要获取用户信息，直接使用openid
            user = self._find_or_create_miniprogram_user(openid, unionid)
            
            return {
                'user_id': user.id,
                'openid': openid,
                'unionid': unionid,
                'session_key': session_key,
                'source_id': openid,
                'source': 2  # 2表示微信小程序登录
            }
            
        except Exception as e:
            # 如果小程序授权失败，尝试网页授权
            """
            if "invalid oauth code" in str(e) or "jscode" in str(e):
                logger.info("小程序授权失败，尝试网页授权方式")
                try:
                    token_info = self.get_access_token(code)
                    access_token = token_info.get('access_token')
                    openid = token_info.get('openid')
                    unionid = token_info.get('unionid')
                    
                    if not access_token or not openid:
                        raise Exception("获取微信授权信息失败")
                    
                    # 获取用户信息
                    user_info = self.get_user_info(access_token, openid)
                    
                    # 查找或创建用户
                    user = self._find_or_create_user(openid, unionid, user_info)
                    
                    logger.info("网页授权成功")
                    
                    return {
                        'user_id': user.id,
                        'openid': openid,
                        'unionid': unionid,
                        'nickname': user_info.get('nickname'),
                        'headimgurl': user_info.get('headimgurl'),
                        'source_id': openid,
                        'source': 2  # 2表示微信网页登录
                    }
                    
                except Exception as web_error:
                    logger.error(f"网页授权也失败: {str(web_error)}")
                    raise e  # 抛出原始的小程序错误
            """
            raise e
    
    def _find_or_create_miniprogram_user(self, openid: str, unionid: Optional[str]) -> CmUser:
        """
        查找或创建微信小程序用户
        """
        ds = db.session
        
        # 先通过openid查找用户
        user = ds.query(CmUser).filter(
            CmUser.source == 2,  # 2表示微信小程序登录
            CmUser.source_id == openid
        ).first()
        
        if user:
            # 如果用户存在，更新unionid（如果之前没有的话）
            if unionid and not user.unionid:
                user.unionid = unionid
                ds.commit()
            return user
        
        # 创建新用户
        user = CmUser(
            source=2,  # 3表示微信小程序登录
            source_id=openid,
            unionid=unionid,
            nickname=None,  # 小程序无法获取用户昵称
            headimgurl=None  # 小程序无法获取头像
        )
        
        ds.add(user)
        ds.commit()
        ds.refresh(user)
        
        return user
    
    def _find_or_create_user(self, openid: str, unionid: Optional[str], user_info: Dict[str, Any]) -> CmUser:
        """
        查找或创建微信用户 (网页授权)
        """
        ds = db.session
        
        # 先通过openid查找用户
        user = ds.query(CmUser).filter(
            CmUser.source == 2,  # 2表示微信网页登录
            CmUser.source_id == openid
        ).first()
        
        if user:
            # 如果用户存在，更新unionid（如果之前没有的话）
            if unionid and not user.unionid:
                user.unionid = unionid
                ds.commit()
            return user
        
        # 创建新用户
        user = CmUser(
            source=2,  # 2表示微信网页登录
            source_id=openid,
            unionid=unionid,
            nickname=user_info.get('nickname'),
            headimgurl=user_info.get('headimgurl'),
            created_at=int(time.time()),
            updated_at=int(time.time())
        )
        
        ds.add(user)
        ds.commit()
        ds.refresh(user)
        
        return user

# 创建全局实例
wechat_service = WeChatService() 